package com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.mods.math;

import java.util.Scanner;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.data.FastMath.FastMath;
import com.nulldev.util.io.IOUtils;

@SuppressWarnings("static-access")
public class baseJSMaths {

	private static final com.nulldev.util.data.FastMath.Math Math = FastMath.get();

	private static class baseJSMathParser {
		private final String str;

		public baseJSMathParser(final String str) {
			this.str = str;
		}

		int pos = -1, ch;

		void nextChar() {
			ch = (++pos < str.length()) ? str.charAt(pos) : -1;
		}

		boolean eat(int charToEat) {
			while (ch == ' ')
				nextChar();
			if (ch == charToEat) {
				nextChar();
				return true;
			}
			return false;
		}

		double parse() {
			nextChar();
			double x = parseExpression();
			if (pos < str.length())
				throw new RuntimeException("Unexpected: " + (char) ch);
			return x;
		}

		double parseExpression() {
			double x = parseTerm();
			for (;;) {
				if (eat('+'))
					x += parseTerm();
				else if (eat('-'))
					x -= parseTerm();
				else if (eat('%'))
					x %= parseTerm();
				else
					return x;
			}
		}

		double parseTerm() {
			double x = parseFactor();
			for (;;) {
				if (eat('*'))
					x *= parseFactor();
				else if (eat('/'))
					x /= parseFactor();
				else
					return x;
			}
		}

		double parseFactor() {
			if (eat('+'))
				return parseFactor();
			if (eat('-'))
				return -parseFactor();

			double x;
			int startPos = this.pos;
			if (eat('(')) { // parentheses
				x = parseExpression();
				eat(')');
			} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
				while ((ch >= '0' && ch <= '9') || ch == '.')
					nextChar();
				x = Double.parseDouble(str.substring(startPos, this.pos));
			} else if (ch >= 'a' && ch <= 'z') { // functions
				while (ch >= 'a' && ch <= 'z')
					nextChar();
				String func = str.substring(startPos, this.pos);
				x = parseFactor();
				if (func.equals("sqrt"))
					x = Math.sqrt(x);
				else if (func.equals("sin"))
					x = Math.sin(Math.toRadians(x));
				else if (func.equals("cos"))
					x = Math.cos(Math.toRadians(x));
				else if (func.equals("tan"))
					x = Math.tan(Math.toRadians(x));
				else
					throw new RuntimeException("Unknown function: " + func);
			} else {
				throw new RuntimeException("Unexpected: " + (char) ch);
			}

			if (eat('^'))
				x = Math.pow(x, parseFactor()); // exponents

			return x;
		}
	}

	public static double eval(final String str) {
		return new baseJSMathParser(str).parse();
	}

	public static void main(String[] args) {
		final Scanner s = new Scanner(System.in);
		while (true) {
			JVM.print("=> ");
			final String line = s.nextLine();
			if (line == null || line.isEmpty())
				continue;
			if (line.equalsIgnoreCase("exit") || line.equalsIgnoreCase("close"))
				break;
			try {
				JVM.println(eval(line));
			} catch (Exception e) {
				JVM.println("Failed to evaluate statement.");
				JVM.errorln(e);
			}
		}
		IOUtils.closeQuietly(s);
	}

}
